﻿/*
 * @Autor: PK
 * @Date: 2021-03-31 16:36:19
 * @Description: 仿真应用，每一个SimulationApplication就是一个仿真路网，因此可以方便地多线程仿真，Sample目录下都是示例，部分代码可以直接使用
 */
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Itinero;
using ShkSimulation.carsharing;
using ShkSimulation.core.dao.input.loader;
using ShkSimulation.core.dao.output.result;
using ShkSimulation.core.dao.statistic;
using ShkSimulation.core.exceptions;
using ShkSimulation.core.template;
using ShkSimulation.core.template.agency;
using ShkSimulation.core.template.customer;
using ShkSimulation.core.template.model;
using ShkSimulation.core.util.osm;
using ShkSimulation.carsharing.impl.elements;
using ShkSimulation.carsharing.impl.model;
using ShkSimulation.carsharing.impl.model.dispatch;

namespace ShkSimulation.carsharing.impl {
    
    public class SimulationInfo {
        
        public Network Network { get; set; }
        
        public NetworkRunInfo NetworkRunInfo { get; set; }
		
        public string SimulationName { get; set; }
		
        public NetworkResultCollector ResultHolder { get; set; }
		
        public NetworkInputData NetworkInputData { get; set; }
        
        private List<string> ProcessInfo { get; set; } = new List<string>();
        
        public Result Result { get; set; }

        internal event Action<string> OnProcessInfoAdd;

        internal void AddProcessInfo(string processInfo) {
            if (OnProcessInfoAdd != null) {
                OnProcessInfoAdd(processInfo);
            }
            ProcessInfo.Add(processInfo);
        }
        
        public List<string> TakeAllProcessInfo() {
            List<string> temp = ProcessInfo;
            ProcessInfo = new List<string>();
            return temp;
        }

        public static SimulationInfo CreateInfo(Network network, string simulationName) {
            SimulationInfo simulationInfo = new SimulationInfo();
            simulationInfo.Network = network;
            simulationInfo.ResultHolder = network.ResultCollector;
            simulationInfo.NetworkInputData = network.NetworkInput;
            simulationInfo.SimulationName = simulationName;
            return simulationInfo;
        }

        public static SimulationInfo CreateByNetworkRunInfo(Network network, string simulationName, NetworkRunInfo runInfo) {
            SimulationInfo result = CreateInfo(network, simulationName);
            result.NetworkRunInfo = runInfo;
            return result;
        }

    }

    public class SimulationApplication {

        protected static readonly Logger logger = LogManager.GetLogger(nameof(Customer));

        public Action<SimulationInfo> AfterInitSuccess { get; set; } = (_) => { };
        public Action<SimulationInfo> BeforeSimulationRun { get; set; } = (_) => { };
        public Action<SimulationInfo> OnSimulationRunning { get; set; } = (_) => { };
        public Action<SimulationInfo> AfterRunCompleted { get; set; } = (_) => { };

        public event Action<Route, Road> OnRouteSuccess;

        public event Action<string> OnProcessInfoAdd;

        public void AddProcessInfo(string info) {
            this.SimulationRunInfo.AddProcessInfo(info);
        }

        public NetworkResultHolder ResultHolder { get; private set; }
        public NetworkEnvironment NetworkInputData { get; private set; }
        public CloneableNetwork SimulationNetwork { get; private set; }
        public SimulationInfo SimulationRunInfo { get; set; }
        private Dictionary<string, object> Information { get; set; } = new Dictionary<string, object>();

        public string SimulationName { get; private set; }

        public SimulationApplication(string filePath, string name) {
            this.SimulationName = name;
            ResultHolder = new NetworkResultHolder();
            NetworkInputData = new NetworkEnvironment(filePath, new ExcelResourceLoader());
            SimulationNetwork = QueueNetwork.GenerateNetwork(NetworkInputData, ResultHolder);
            RegisterAction();
        }
        
        public SimulationApplication(InputConfig config, ResourceLoader resourceLoader, string name) {
            this.SimulationName = name;
            ResultHolder = new NetworkResultHolder();
            NetworkInputData = new NetworkEnvironment(config, resourceLoader);
            SimulationNetwork = QueueNetwork.GenerateNetwork(NetworkInputData, ResultHolder);
            RegisterAction();
        }

        private void RegisterAction() {
            SimulationRunInfo = SimulationInfo.CreateInfo(SimulationNetwork, SimulationName);
            SimulationRunInfo.OnProcessInfoAdd += info => {
                if (OnProcessInfoAdd != null) {
                    OnProcessInfoAdd(info);
                }
            };

            SimulationNetwork.AfterInitSuccess += info => {
                info.Network.ResultCollector.DataReporterGenerator.DataReporterGenerateAction += SimulationApplication.AddDataReporters;
                info.Network.ResultCollector.InitStatistics(info.Network);

                EnableDispatchAndNavigate(info);
                SimulationRunInfo.NetworkRunInfo = info;
                SimulationRunInfo.AddProcessInfo(info.Message);
                AfterInitSuccess(SimulationRunInfo);
            };
            SimulationNetwork.BeforeSimulationRun += info => {
                SimulationRunInfo.NetworkRunInfo = info;
                Information["StartRunTime"] = DateTime.Now;
                SimulationRunInfo.AddProcessInfo(info.Message);
                BeforeSimulationRun(SimulationRunInfo);
            };
            SimulationNetwork.OnSimulationRunning += info => {
                SimulationRunInfo.NetworkRunInfo = info;
                DateTime start = (DateTime)Information["StartRunTime"];
                SimulationRunInfo.AddProcessInfo($"{info.Message}，耗时{DateTime.Now - start}");
                OnSimulationRunning(SimulationRunInfo);
            };
            SimulationNetwork.AfterRunCompleted += info => {
                SimulationRunInfo.NetworkRunInfo = info;
                SimulationRunInfo.Result = SimulationNetwork.GetCurrentResult("Final data！");
                SimulationRunInfo.AddProcessInfo(info.Message);
                AfterRunCompleted(SimulationRunInfo);
            };            

        }
        
        private void EnableDispatchAndNavigate(NetworkRunInfo networkRunInfo) {
            NetworkInputData inputData = networkRunInfo.Network.NetworkInput;
            Dictionary<int, Agency> stationDictionary = inputData.ClassifiedAgencies[CarsharingConfig.StationResourceKey]
                .ToDictionary(each => each.UniqueId, each => each);
            Dictionary<int, Agency> roadDictionary = inputData.ClassifiedAgencies[CarsharingConfig.RoadResourceKey]
                .ToDictionary(each => each.UniqueId, each => each);
            // 开启调度
            if (inputData.ConfigInstance.GetConfigValue<bool>(CarsharingConfig.DispatchKey)) {
                foreach (var abstractStation in stationDictionary.Values) {
                    var station = (Station) abstractStation;
                    station.DispatchHelper = new DefaultDispatchHelper(networkRunInfo.Network as QueueNetwork);
                }
            }
            // 开启导航
            string path = inputData.ConfigInstance.GetConfigValue<string>(CarsharingConfig.NavigateRouterPathKey);
            if (!inputData.ConfigInstance.GetConfigValue<bool>(CarsharingConfig.NavigateEnableKey) 
                || string.IsNullOrEmpty(path)) {
                return;
            }
            NavigateType navigateType = inputData.ConfigInstance.GetConfigValue<NavigateType>(CarsharingConfig.NavigateTypeKey);
            logger.Info($"启用导航,加载导航文件:{path}");
            if (File.Exists(path)) {
                OsmRouterData osmRouterData = OsmLoadUtil.LoadRouterByOsmFile(path, NavigateProfileProvider.Profiles);
                if (!osmRouterData.Success) {
                    logger.Error(osmRouterData.Message);
                    throw new ErrorParamException(osmRouterData.Message);
                }
                using FileStream fileStream = File.OpenRead(path); 
                RouterDb routerDb = RouterDb.Deserialize(fileStream);
                Router router = new Router(routerDb);
                foreach (var (_, abstractRoad) in roadDictionary) {
                    Road road = (Road)abstractRoad;
                    RoadServiceRateHelper helper = new RoadServiceRateHelper(road, router, NavigateProfileProvider.GetProfileByNavigateType(navigateType).Profile);
                    road.ServiceRateHelper = helper;
                    helper.OnRouteSuccess += this.OnRouteSuccess;
                    road.LastStation = stationDictionary[road.LastStationId] as Station;
                    road.NextStation = stationDictionary[road.NextStationId] as Station;
                }
            }
        }

        public static Dictionary<string, List<DataReporter>> AddDataReporters(Network network) {
            QueueNetwork queueNetwork = (QueueNetwork)network;
            NetworkInputData inputData = network.NetworkInput;
            Dictionary<string, List<DataReporter>> defaultAction = new Dictionary<string, List<DataReporter>>();
            List<DataReporter> vehicles = new List<DataReporter>(queueNetwork.Vehicles);
            List<DataReporter> dispatchers = new List<DataReporter>(queueNetwork.Dispatchers);
            defaultAction.Add("车辆", vehicles);
            defaultAction.Add("调度员", dispatchers);
            return defaultAction;
        }

        public bool Init(DateTime startTime) {
            return SimulationNetwork.Init(startTime).Success;
        }
        
        public bool Init() {
            return Init(DateTime.MinValue);
        }
        
        public Result StartRun(TimeSpan runDays, TimeSpan observeSpan) {
            Stopwatch initalTimer = new Stopwatch();
            initalTimer.Start();
            this.Running = true;
            SimulationNetwork.RunWithTerminate(runDays, observeSpan);
            initalTimer.Stop();
            this.Running = false;
            string result = string.Format("运行完成，时间: {0} s {1} ms", initalTimer.ElapsedMilliseconds / 1000, 
                initalTimer.ElapsedMilliseconds % 1000);
            SimulationRunInfo.AddProcessInfo(result);
            return SimulationRunInfo.Result;
        }

        public Result StartRun(int runDays, int observeSpan = 1) {
            return StartRun(TimeSpan.FromDays(runDays), TimeSpan.FromDays(observeSpan));
        }
        
        public Result ConsinousRun(TimeSpan totalSecond, TimeSpan observeSeconds) {
            Stopwatch initalTimer = new Stopwatch();
            initalTimer.Start();
            this.Running = true;
            SimulationNetwork.ContinousRun(totalSecond, observeSeconds);
            initalTimer.Stop();
            this.Running = false;
            string result = string.Format("运行完成，时间: {0} s {1} ms", initalTimer.ElapsedMilliseconds / 1000, 
                initalTimer.ElapsedMilliseconds % 1000);
            SimulationRunInfo.AddProcessInfo(result);
            return SimulationRunInfo.Result;
        }
        
        public Result ConsinousRun(int totalSecond, int observeSeconds = 10) {
            return ConsinousRun(TimeSpan.FromSeconds(totalSecond), TimeSpan.FromSeconds(observeSeconds));
        }

        public bool Running { get; private set; }

        ~SimulationApplication() {
            OnProcessInfoAdd = null;
            OnRouteSuccess = null;
            AfterInitSuccess = null;
            BeforeSimulationRun = null;
            OnSimulationRunning = null;
            AfterRunCompleted = null;
            ResultHolder = null;
            NetworkInputData = null;
            SimulationNetwork = null;
            SimulationRunInfo = null;
        }

    }
}
